package com.glitter.player.ioc;

import com.glitter.player.ioc.annotation.PPAutowired;
import com.glitter.player.ioc.annotation.PPController;
import com.glitter.player.ioc.annotation.PPRequestMapping;
import com.glitter.player.ioc.annotation.PPService;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @Description:
 * @ClassName:PPDispatcherServlet
 * @Author:Player
 * @Date:2019-06-14 10:15
 **/
public class PPDispatcherServlet extends HttpServlet {

    public PPDispatcherServlet() {
        super();
    }

    /*和web.xml中的param-name保持一致*/
    private static final String LOCATION = "contextConfigLocation";

    //保存所有的配置信息
    private Properties properties = new Properties();

    //保存所有被扫描到的类名
    private List<String> classNameList = new ArrayList<String>();

    //核心IOC容器，保存初始化的bean
    private Map<String, Object> iocMap = new HashMap<String, Object>();

    //保存所有的URL映射方法的映射关系
    private Map<String, Method> handlerMapper = new HashMap<String, Method>();


    /*初始化Servlet的时候初始化spring*/
    @Override
    public void init() throws ServletException {
        //加载配置文件
        doLoadConfig();

        //扫描相关的类
        doScanner(properties.getProperty("scanPackage"));

        //初始化所有相关的实例,并保存到IOC容器中
        doInstance();

        //依赖注入
        doAutowired();

        //初始化方法
        initHandlerMapper();


        super.init();
    }


    /*get访问的操作*/
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        doDispatch(req,resp);
    }

    private void doDispatch(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws IOException {
        if(this.handlerMapper.isEmpty())
            return;
        String url=httpServletRequest.getRequestURI();
        String contextPath=httpServletRequest.getContextPath();
        url=url.replaceAll(contextPath,"").replaceAll("/+","/");

        if(!this.handlerMapper.containsKey(url)){
            httpServletResponse.getWriter().write("404!");
            return;
        }
        Method method=this.handlerMapper.get(url);
        //获取方法参数列表
        Class<?>[] parameterTypes=method.getParameterTypes();
        //获取请求列表
        Map<String,String[]> parameterMap=httpServletRequest.getParameterMap();
        //保存参数值
        Object [] paramValues=new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            //根据参数名，做某些处理
            Class parameterType=parameterTypes[i];
            if(parameterType==HttpServletRequest.class){
                paramValues[i]=httpServletRequest;
            }
            if(parameterType==HttpServletResponse.class){
                paramValues[i]=httpServletResponse;
            }
            if(parameterType==String.class){
                for (Map.Entry<String,String[]> param:parameterMap.entrySet()){
                    String value=Arrays.toString(param.getValue())
                            .replaceAll("[\\[\\]]","")
                            .replaceAll(",\\s",",");
                    paramValues[i]=value;
                }
            }

        }
        try {
            String beanName=lowerFirstCase(method.getDeclaringClass().getSimpleName());
            method.invoke(this.iocMap.get(beanName),paramValues);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    /*加载配置文件*/
    private void doLoadConfig() {
        InputStream inputStream = null;
        //读配置
        try {
            inputStream = this.getClass().getClassLoader().getResourceAsStream(this.getInitParameter(PPDispatcherServlet.LOCATION));
            properties.load(inputStream);

        } catch (IOException e) {
            System.out.println("配置读取失败！");
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doScanner(String packageName) {
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        assert url != null;
        File dir = new File(url.getFile());
        for (File fil : Objects.requireNonNull(dir.listFiles())) {
            //如果是文件夹就递归
            if (fil.isDirectory()) {
                doScanner(packageName + "." + fil.getName());
            } else {
                classNameList.add(packageName + "." + fil.getName().replace(".class", "").trim());
            }
        }
    }

    private void doInstance() {
        if (classNameList.size() == 0)
            return;
        try {
            for (String classname : classNameList) {
                Class<?> classObj = Class.forName(classname);
                if (classObj.isAnnotationPresent(PPController.class)) {
                    String beanName = lowerFirstCase(classObj.getSimpleName());
                    iocMap.put(beanName, classObj.newInstance());
                    continue;
                }
                if (classObj.isAnnotationPresent(PPService.class)) {
                    PPService service = classObj.getAnnotation(PPService.class);
                    String beanName = service.value();
                    //如果用户自己设置了名字，那就用用户的
                    if (!"".equals(beanName.trim())) {
                        iocMap.put(beanName, classObj.newInstance());
                        continue;
                    }
                    //如果没有设置，那就生成
                    Class<?>[] interfaces = classObj.getInterfaces();
                    for (Class<?> c : interfaces) {
                        iocMap.put(c.getName(), classObj.newInstance());
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void doAutowired() {
        if (iocMap.isEmpty())
            return;
        for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(PPAutowired.class))
                    continue;
                PPAutowired autowired = field.getAnnotation(PPAutowired.class);
                String beanName = autowired.value().trim();

                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                //设置私有访问
                field.setAccessible(true);

                try {
                    field.set(entry.getKey(), iocMap.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void initHandlerMapper() {
        if (!iocMap.isEmpty()) {
            for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
                Class<?> classObj = entry.getValue().getClass();
                if (!classObj.isAnnotationPresent(PPController.class))
                    continue;
                String baseUrl = "";
                //获取Controller的url
                if (classObj.isAnnotationPresent(PPRequestMapping.class)) {
                    PPRequestMapping requestMapping = classObj.getAnnotation(PPRequestMapping.class);
                    baseUrl = requestMapping.value();
                }
                //获取Methods的url
                Method[] methods = classObj.getMethods();

                for (Method method : methods) {
                //如果没有加注解直接忽略
                    if(!method.isAnnotationPresent(PPRequestMapping.class))
                        continue;
                    PPRequestMapping requestMapping=method.getAnnotation(PPRequestMapping.class);
                    String url=("/"+baseUrl+"/"+requestMapping.value()).replaceAll("/+","/");
                    handlerMapper.put(url,method);
                    System.out.println("mapped:"+url+"   method:"+method.getName());
                }
            }
        }
    }


    /*首字母小写*/
    private String lowerFirstCase(String content) {
        char[] chars = content.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}
